﻿using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
using WickedSick.ClientComponents.Music.SDBService;

namespace WickedSick.ClientComponents.Music
{
    [Serializable]
    public class MediaBank : ISerializable
    {
        private DateTime _RecentUpdateTime;
        private Dictionary<int, Artist> _AllArtists;
        private Dictionary<int, Song> _AllSongs;
        private Dictionary<int, Playlist> _Playlists;

        public MediaBank()
        {
            _RecentUpdateTime = new DateTime();
            _AllArtists = new Dictionary<int, Artist>();
            _AllSongs = new Dictionary<int, Song>();
            _Playlists = new Dictionary<int, Playlist>();
        }

        public MediaBank(SerializationInfo info, StreamingContext context)
        {
            this._RecentUpdateTime = info.GetDateTime("RecentUpdateTime");
            this._AllArtists = (Dictionary<int, Artist>)info.GetValue("Artists", typeof(Dictionary<int, Artist>));
            this._Playlists = (Dictionary<int, Playlist>)info.GetValue("Playlists", typeof(Dictionary<int, Playlist>));
        }

        public Dictionary<int, Artist> Artists
        {
            get { return _AllArtists; }
        }

        public Dictionary<int, Song> Songs
        {
            get { return _AllSongs; }
        }

        public Dictionary<int, Playlist> Playlists
        {
            get { return _Playlists; }
        }

        internal void LoadFullBank(ref List<Artist> artists, ref Dictionary<int, Album> albums, ref Dictionary<int, Song> songs, ref List<int> library)
        {
            _AllSongs = new Dictionary<int, Song>();
            foreach (int j in library)
            {
                songs[j].SongLocation = SongLocations.Library;
                _AllSongs.Add(j, songs[j]);
            }
            
            for (int i = 0; i < artists.Count; i++)
            {
                _AllArtists.Add(artists[i].ID, artists[i]);
            }
            artists.Clear();
            
            foreach (int k in _AllArtists.Keys)
            {
                foreach (int k2 in _AllArtists[k].Albums.Keys)
                {
                    _AllArtists[k].Albums[k2].Name = albums[k2].Name;
                    _AllArtists[k].Albums[k2].ReleaseDate = albums[k2].ReleaseDate;
                    _AllArtists[k].Albums[k2].Parent = _AllArtists[k];
                    foreach (int k3 in albums[k2].Songs.Keys)
                    {
                        Song s = new Song();
                        s.ID = k3;
                        s.Duration = songs[k3].Duration;
                        s.ParentAlbum = _AllArtists[k].Albums[k2];
                        s.ParentArtist = _AllArtists[k];
                        s.SongLocation = songs[k3].SongLocation;
                        s.Title = songs[k3].Title;
                        s.TrackNumber = songs[k3].TrackNumber;
                        s.UploadDate = songs[k3].UploadDate;
                        _AllArtists[k].Albums[k2].Songs.Add(k3, s);
                    }
                }
            }
            albums.Clear();
            songs.Clear();
            _RecentUpdateTime = DateTime.Now;
        }
        
        internal void LoadPlaylists(ref List<Playlist> playlists)
        {
            this._Playlists = new Dictionary<int, Playlist>(playlists.Count);
            foreach (Playlist p in playlists)
            {
                this._Playlists.Add(p.ID, p);
                //Finish loading rest of each song
                //Currently each song only has SongID
                if (_AllSongs.Count > 0)
                {
                    foreach (Song s in p.Songs)
                    {
                        if (_AllSongs.ContainsKey(s.ID))
                        {
                            s.ParentArtist = _AllSongs[s.ID].ParentArtist;
                            s.ParentAlbum = _AllSongs[s.ID].ParentAlbum;
                            s.SongLocation = _AllSongs[s.ID].SongLocation;
                            s.Title = _AllSongs[s.ID].Title;
                            s.TrackNumber = _AllSongs[s.ID].TrackNumber;
                            s.UploadDate = _AllSongs[s.ID].UploadDate;
                            s.Duration = _AllSongs[s.ID].Duration;
                        }
                    }
                }
                //All songs should have metadata loaded
            }
            playlists.Clear();
        }

        internal void ApplyBankUpdates(ref List<Audit> audits)
        {
            foreach (Audit audit in audits)
            {
                switch (audit.AuditType)
                {
                    case AuditTypes.Add:
                        AddAudit(audit);
                        break;
                    case AuditTypes.Modify:
                        ModifyAudit(audit);
                        break;
                    case AuditTypes.Remove:
                        RemoveAudit(audit);
                        break;
                    default:
                        break;
                }
                _RecentUpdateTime = LatestUpdateTime(_RecentUpdateTime, audit.AuditDate);
            }
            audits.Clear();
        }

        private DateTime LatestUpdateTime(DateTime d1, DateTime d2)
        {
            return ((d1.Ticks > d2.Ticks) ? d1 : d2);
        }

        private void AddAudit(Audit audit)
        {
            string delim = "<|>";
            string delim2 = ">|<";
            switch (audit.ObjectType)
            {
                case ObjectTypes.Album:
                    Album a = new Album(audit.ObjectID, audit.NewValue);

                    try
                    {
                        string[] tokens = audit.NewValue.Split(new string[] {delim2}, StringSplitOptions.None);
                        int artist_id = Convert.ToInt32(tokens[1]);
                        _AllArtists[artist_id].Albums.Add(a.ID, a);
                        a.Parent = _AllArtists[artist_id];
                    }
                    catch (IndexOutOfRangeException ioor)
                    {
                        throw new Exception("NewValue does not follow protocol.", ioor);
                    }
                    catch (KeyNotFoundException knf)
                    {
                        throw new Exception("Artist ID not in existence.", knf);
                    }
                    
                    break;
                case ObjectTypes.Artist:
                    Artist ar = new Artist(audit.ObjectID, audit.NewValue);
                    _AllArtists.Add(ar.ID, ar);
                    break;
                case ObjectTypes.Playlist:
                    Playlist p = new Playlist(audit.ObjectID, audit.NewValue);
                    //TODO
                    break;
                case ObjectTypes.Song:
                    Song s = new Song(audit.ObjectID, audit.NewValue);

                    try
                    {
                        string[] tokens = audit.NewValue.Split(new string[] { delim }, StringSplitOptions.None);
                        int artist_id = Convert.ToInt32(tokens[1]);
                        int album_id = Convert.ToInt32(tokens[2]);
                        _AllArtists[artist_id].Albums[album_id].Songs.Add(s.ID, s);
                        s.ParentAlbum = _AllArtists[artist_id].Albums[album_id];
                        s.ParentArtist = _AllArtists[artist_id];
                    }
                    catch (IndexOutOfRangeException ioor)
                    {
                        throw new Exception("NewValue does not follow protocol.", ioor);
                    }
                    catch (KeyNotFoundException knf)
                    {
                        throw new Exception("Artist or Album ID not in existence.", knf);
                    }
                    
                    break;
                default:
                    break;
            }
        }

        private void ModifyAudit(Audit audit)
        {
            switch (audit.ObjectType)
            {
                case ObjectTypes.Album:
                    foreach (int k in _AllArtists.Keys)
                    {
                        _AllArtists[k].ModifyAlbumField(audit.ObjectID, audit.FieldName, audit.NewValue);
                    }
                    break;
                case ObjectTypes.Artist:
                    _AllArtists[audit.ObjectID].ModifyField(audit.FieldName, audit.NewValue);
                    break;
                case ObjectTypes.Playlist:
                    _Playlists[audit.ObjectID].ModifyField(audit.FieldName, audit.NewValue);
                    break;
                case ObjectTypes.Song:
                    foreach (int k in _AllArtists.Keys)
                    {
                        _AllArtists[k].ModifySongField(audit.ObjectID, audit.FieldName, audit.NewValue);
                    }
                    break;
                default:
                    break;
            }
        }

        private void RemoveAudit(Audit audit)
        {
            switch (audit.ObjectType)
            {
                case ObjectTypes.Album:
                    foreach (int k in _AllArtists.Keys)
                    {
                        _AllArtists[k].RemoveAlbum(audit.ObjectID);
                    }
                    break;
                case ObjectTypes.Artist:
                    if (_AllArtists.ContainsKey(audit.ObjectID))
                    {
                        _AllArtists.Remove(audit.ObjectID);
                    }
                    break;
                case ObjectTypes.Playlist:
                    //TODO
                    break;
                case ObjectTypes.Song:
                    foreach (int k in _AllArtists.Keys)
                    {
                        _AllArtists[k].RemoveSong(audit.ObjectID);
                    }
                    break;
                default:
                    break;
            }
        }

        internal void AssociateOwnerObjects()
        {
            foreach (Artist artist in _AllArtists.Values)
            {
                foreach (Album album in artist.Albums.Values)
                {
                    album.Parent = artist;
                    foreach (Song song in album.Songs.Values)
                    {
                        song.ParentAlbum = album;
                        song.ParentArtist = artist;
                    }
                }
            }
        }

        internal void BuildAllSongs()
        {
            this._AllSongs = new Dictionary<int, Song>();
            foreach (Artist artist in _AllArtists.Values)
            {
                foreach (Album album in artist.Albums.Values)
                {
                    foreach (Song song in album.Songs.Values)
                    {
                        this._AllSongs.Add(song.ID, song);
                    }
                }
            }
        }

        #region ISerializable Members

        public void GetObjectData(SerializationInfo info, StreamingContext context)
        {
            info.AddValue("RecentUpdateTime", this._RecentUpdateTime);
            info.AddValue("Artists", this.Artists);
            info.AddValue("Playlists", this.Playlists);
        }

        #endregion
    }
}
